﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WiimoteLib;
using System.Diagnostics;
using Sanford.Multimedia.Midi;
using System.IO;
using System.Collections;
using System.Globalization;

namespace WiiDrumSynth
{
    /// <summary>
    /// The Main form for the WiiDrumSynth Application
    /// </summary>
    public partial class MainForm : Form
    {
        private delegate void SetTextDelegate(ToolStripStatusLabel label, string text);
        private delegate void SetBatteryDelegate(ToolStripProgressBar bar, int value);

        private WiimoteCollection wiimotes;
        private MidiSynthLib.Synth synth;
        private float lastZ;
        private float extensionLastZ;
        private ToolStripStatusLabel[] wiiStatus;
        private ToolStripProgressBar[] wiiBattery;
        private Hashtable wiimoteMap;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
            synth = new MidiSynthLib.Synth(9, 118);

            wiiStatus = new ToolStripStatusLabel[4];
            wiiStatus[0] = wiiStatus1;
            wiiStatus[1] = wiiStatus2;
            wiiStatus[2] = wiiStatus3;
            wiiStatus[3] = wiiStatus4;

            wiiBattery = new ToolStripProgressBar[4];
            wiiBattery[0] = wiiBattery1;
            wiiBattery[1] = wiiBattery2;
            wiiBattery[2] = wiiBattery3;
            wiiBattery[3] = wiiBattery4;

            wiimoteMap = new Hashtable();

            InitializeWiimotes();
        }

        /// <summary>
        /// Initializes the wiimotes.
        /// </summary>
        private void InitializeWiimotes()
        {
            wiimoteMap.Clear();
            for (int i = 0; i < 4; i++)
            {
                wiiStatus[i].Text = String.Format(CultureInfo.CurrentCulture, "{0}: Off", i);
                wiiBattery[i].Value = 0;
            }

            wiimotes = new WiimoteCollection();
            FindWiimotes();
            for (int i = 0; i < wiimotes.Count; i++)
            {
                Wiimote wiimote = wiimotes[i];

                wiimoteMap.Add(wiimote.HIDDevicePath, i);

                wiiStatus[i].Text = String.Format(CultureInfo.CurrentCulture, "{0}: On", i);
                wiiBattery[i].Value = (int)wiimote.WiimoteState.Battery;

                wiimote.WiimoteChanged += new EventHandler<WiimoteChangedEventArgs>(wiimote_WiimoteChanged);
                wiimote.WiimoteExtensionChanged += new EventHandler<WiimoteExtensionChangedEventArgs>(wiimote_WiimoteExtensionChanged);

                try
                {
                    wiimote.Connect();
                    if (wiimote.WiimoteState.ExtensionType == ExtensionType.Nunchuk)
                    {
                        wiimote.SetReportType(InputReport.IRExtensionAccel, IRSensitivity.Maximum, true);
                    }
                    else if (wiimote.WiimoteState.ExtensionType == ExtensionType.None)
                    {
                        wiimote.SetReportType(InputReport.IRAccel, IRSensitivity.Maximum, true);
                    }
                    wiimote.SetLEDs(i + 1);
                }
                catch (WiimoteException ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// Handles the WiimoteExtensionChanged event of the wiimote control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="WiimoteLib.WiimoteExtensionChangedEventArgs"/> instance containing the event data.</param>
        private void wiimote_WiimoteExtensionChanged(object sender, WiimoteExtensionChangedEventArgs e)
        {
            Wiimote wiimote = sender as Wiimote;
            if (e.Inserted && e.ExtensionType == ExtensionType.Nunchuk)
            {
                wiimote.SetReportType(InputReport.IRExtensionAccel, IRSensitivity.Maximum, true);
            }
            else
            {
                wiimote.SetReportType(InputReport.IRAccel, IRSensitivity.Maximum, true);
            }

        }

        /// <summary>
        /// Sets the text for a <see cref="ToolStripStatusLabel" /> control.
        /// </summary>
        /// <param name="label">The label.</param>
        /// <param name="text">The text.</param>
        private void SetText(ToolStripStatusLabel label, string text)
        {
            label.Text = text;
        }

        /// <summary>
        /// Sets the value for a <see cref="ToolStripProgressBar" /> control.
        /// </summary>
        /// <param name="bar">The bar.</param>
        /// <param name="value">The value.</param>
        private void SetBattery(ToolStripProgressBar bar, int value)
        {
            bar.Value = value;
        }

        /// <summary>
        /// Handles the WiimoteChanged event of the wiimote control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="WiimoteLib.WiimoteChangedEventArgs"/> instance containing the event data.</param>
        private void wiimote_WiimoteChanged(object sender, WiimoteChangedEventArgs e)
        {
            //TODO: This method is too long. Need to re-factor.
            //TODO: There is lag introduced somewhere in this method or the methods it calls. Need to investigate and mitigate.
            int id = (int)wiimoteMap[((Wiimote)sender).HIDDevicePath];

            string text = String.Format(CultureInfo.CurrentCulture, "{0}: On", id);
            Invoke(new SetTextDelegate(SetText), new object[] { wiiStatus[id], text });
            Invoke(new SetBatteryDelegate(SetBattery), new object[] { wiiBattery[id], (int)e.WiimoteState.Battery });

            float z = e.WiimoteState.AccelState.Values.Z;
            float extZ = e.WiimoteState.NunchukState.AccelState.Values.Z;


            if (lastZ < 0 && z > 0)
            {
                float velocity = (lastZ * -1) * 127 / 5;
                if (velocity > 127)
                {
                    velocity = 127;
                }
                if (velocity < 0)
                {
                    velocity = 0;
                }

                int note = 46; //default - open hi-hat
                if (e.WiimoteState.ButtonState.A)
                {
                    //A = high cymbal
                    note = 55;
                }
                if (e.WiimoteState.ButtonState.B)
                {
                    //B = snare
                    note = 38;
                }
                if (e.WiimoteState.ButtonState.Left)
                {
                    //Left = high tom
                    note = 50;
                }
                if (e.WiimoteState.ButtonState.Down)
                {
                    //down = mid tom
                    note = 47;
                }
                if (e.WiimoteState.ButtonState.Right)
                {
                    //right = low tom
                    note = 43;
                }
                //filter out "false" hits
                if (velocity > 40.0)
                {
                    synth.PlayNote(note, (int)velocity);
                }
            }

            if (extensionLastZ < 0 && extZ > 0)
            {
                float velocity = (extensionLastZ * -1) * 127 / 3;
                if (velocity > 127)
                {
                    velocity = 127;
                }
                if (velocity < 0)
                {
                    velocity = 0;
                }

                int note = 46; //default = open hi-hat
                if (e.WiimoteState.NunchukState.Z)
                {
                    //Z = snare
                    note = 38;
                }
                if (e.WiimoteState.NunchukState.C)
                {
                    //C = low cymbal
                    note = 49;
                }
                if (e.WiimoteState.NunchukState.Joystick.X < -0.1)
                {
                    //Left = high tom
                    note = 50;
                }
                if (e.WiimoteState.NunchukState.Joystick.Y < -0.1)
                {
                    //Down = mid tom
                    note = 47;
                }
                if (e.WiimoteState.NunchukState.Joystick.X > 0.1)
                {
                    //Right = low tom
                    note = 43;
                }
                //filter out "false" hits
                if (velocity > 40.0)
                {
                    synth.PlayNote(note, (int)velocity);
                }
            }

            if (z < lastZ || z > 0)
            {
                lastZ = z;
            }
            if (extZ < extensionLastZ || extZ > 0)
            {
                extensionLastZ = extZ;
            }
        }

        /// <summary>
        /// Finds the wiimotes.
        /// </summary>
        private void FindWiimotes()
        {
            try
            {
                wiimotes.FindAllWiimotes();
            }
            catch (WiimoteNotFoundException ex)
            {
                Debug.WriteLine("No Wiimotes found: " + ex.Message);
            }
            catch (WiimoteException ex)
            {
                Debug.WriteLine("Error: " + ex.Message);
            }
        }

        /// <summary>
        /// Handles the Click event of the button1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void button1_Click(object sender, EventArgs e)
        {
            InitializeWiimotes();
        }

        /// <summary>
        /// Handles the Click event of the mIDISetupToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mIDISetupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MidiOptions options = new MidiOptions();
            options.OutDeviceId = synth.OutputDeviceId;
            options.ProgramId = synth.MidiProgram;
            options.ChannelId = synth.MidiChannel;

            if (options.ShowDialog() == DialogResult.OK)
            {
                synth.OutputDeviceId = options.OutDeviceId;
                synth.MidiChannel = options.ChannelId;
                synth.MidiProgram = options.ProgramId;
            }
        }

        /// <summary>
        /// Handles the FormClosing event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            for (int i = 0; i < wiimotes.Count; i++)
            {
                Wiimote wiimote = wiimotes[i];

                wiimote.WiimoteChanged -= wiimote_WiimoteChanged;
                wiimote.WiimoteExtensionChanged -= wiimote_WiimoteExtensionChanged;
            }
            synth.Dispose();
        }
    }
}
